package com.foo.designpattern.prototype;

import java.io.Serializable;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;

/**
 * com.foo.designpattern.prototype
 * Created with IntelliJ IDEA.
 * Created by jiang on 2016-04-26 23:50.
 *
 * @Description 原型：齐天大圣的本尊
 * 实现Cloneable接口，可以被克隆
 */
public class Monkey implements Cloneable, Serializable {
    // 身高，在这里初始化，不再添加 setter 方法
    private int height = 160;
    // 体重，在这里直接初始化，不再添加 setter 方法
    private int weight = 50;
    // 生日，通过构造函数初始化
    private Date birthDate;
    // 金箍棒，通过构造函数初始化
    private GoldRingedStaff goldRingedStaff;

    /**
     * 构造函数
     * 获取大圣的生日和金箍棒
     */
    public Monkey() {
        this.birthDate = new Date();
        this.goldRingedStaff = new GoldRingedStaff();
    }

    /**
     * 克隆方法：大圣施展身外身法术
     * 这里是浅克隆(浅拷贝)：只负责克隆按值传递的数据，而不复制它所引用的对象，换言之，所有的对其他对象的引用都仍然指向原来的对象(比如金箍棒)。
     * 在浅克隆的情况下，引用的对象(金箍棒)仍然指向原来的对象，相当于众多身外身和本尊共同持有一个金箍棒，显然是不对的，为解决这种情况就要用到深克隆。
     *
     * @return 克隆大圣或者null
     */
    public Object clone() {
        Monkey monkey = null;
        try {
            monkey = (Monkey) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        } finally {
            return monkey;
        }

    }

    /**
     * 克隆方法：大圣施展身外身法术
     * 这里是深克隆(深拷贝)：深度克隆把要复制的对象所引用的对象都复制了一遍，而这种对被引用到的对象的复制叫做间接复制。
     * 这种情况下，在施展身外身时，同时也克隆了一个金箍棒，每个身外身都会有一个克隆的金箍棒。
     * 在决定以深度克隆的方式复制一个对象的时候，必须决定对间接复制的对象时采取浅度克隆还是继续采用深度克隆。
     * 在深度克隆的过程中，很可能会出现循环引用的问题，必须小心处理。
     *
     * @return 深克隆的大圣，比浅克隆的更像本尊
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public Object deepClone() throws IOException, ClassNotFoundException {
        // 进行序列化，将对象写到流中,此处写到流里的是对象的一个拷贝
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(this);
        // 进行反序列化，将对象从流里读出来
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        return objectInputStream.readObject();
    }

    public int getHeight() {
        return height;
    }

    public int getWeight() {
        return weight;
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public GoldRingedStaff getGoldRingedStaff() {
        return goldRingedStaff;
    }

}
